﻿//==============================================================================  
//Copyright (C) 2012-2015 9UN.ORG. All rights reserved. 
//GUID：46172595-6b69-4526-b9da-426d663f5b05
//CLR Version: 4.0.30319.18033
//Code Author：Kevin Wang
//Contact：Email(Admin@9un.org),QQ(265382 or 74344)
//Filename：MediaLibraryFactory
//Namespace：RuanRang.Media.Core
//Functions：MediaLibraryFactory  
//Created by Kevin Wang at 2013/2/2 23:05:09 http://blog.9un.org
//============================================================================== 

#region Usings

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using RuanRang.Media.Core.Exceptions;
using RuanRang.Media.Core.Internal.DataStructures;
using log4net;

#endregion

namespace RuanRang.Media.Core.Internal.Interop
{
    /// <summary>
    /// Thunks to original library.
    /// </summary>
    public class LibVlcCoreInterop : BaseInterop
    {
        /// <summary>
        /// Initializes static members of the <see cref="LibVlcCoreInterop"/> class.
        /// </summary>
        static LibVlcCoreInterop()
        {
            Logger = LogManager.GetLogger(typeof(LibVlcCoreInterop));
        }

        #region Errors

        /// <summary>
        /// Libvlc_errmsg_unmanageds this instance.
        /// </summary>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_errmsg")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_errmsg_unmanaged();

        /// <summary>
        /// Return a human-readable error message for the last LibVLC error in the calling thread.
        /// </summary>
        /// <returns>A human-readable error message for the last LibVLC error in the calling thread. 
        /// Or <code>null</code> if there is no any error. 
        /// </returns>
        public static string libvlc_errmsg()
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_errmsg");
            }
            var result = libvlc_errmsg_unmanaged();
            if (result != IntPtr.Zero)
            {
                var size = 0;
                const int max = 1024 * 4;
                byte[] message = null;
                while (size < max)
                {
                    var b = Marshal.ReadByte(result, size);
                    if (b == 0x0)
                    {
                        message = new byte[size];
                        break;
                    }
                    size++;
                }
                if (message != null)
                {
                    Marshal.Copy(result, message, 0, size);
                    return Encoding.UTF8.GetString(message);
                }
                throw new VlcInternalException("Message exceeds maximum limit, probably libvlc_errmsg returned bad pointer.");
            }
            return null;
        }

        /// <summary>
        /// Clears the LibVLC error status for the current thread.
        /// </summary>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_clearerr")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void libvlc_clearerr_unmanaged();

        /// <summary>
        /// Libvlc_clearerrs this instance.
        /// </summary>
        public static void libvlc_clearerr()
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_clearerr");
            }
            libvlc_clearerr_unmanaged();
        }

        #endregion

        #region Core objects

        /// <summary>
        /// Find a named object and increment its refcount. Don't forget to call __vlc_object_release()
        /// after using found object.
        /// </summary>
        [DllImport("libvlccore", ExactSpelling = true, EntryPoint = "vlc_object_find_name")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr vlc_object_find_name_unmanaged(IntPtr vlc_object_t, IntPtr name, int flags);

        /// <summary>
        /// Vlc_object_find_names the specified vlc_object_t.
        /// </summary>
        /// <param name="vlc_object_t">The vlc_object_t.</param>
        /// <param name="name">The name.</param>
        /// <param name="flags">The flags.</param>
        /// <returns>IntPtr.</returns>
        /// <exception cref="System.ArgumentNullException">name</exception>
        public static IntPtr vlc_object_find_name(IntPtr vlc_object_t, string name, int flags)
        {
            if (CallTraceEnabled)
            {
                TraceCall("vlc_object_find_name");
            }
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            var namePtr = Marshal.StringToHGlobalAnsi(name);
            try
            {
                return vlc_object_find_name_unmanaged(vlc_object_t, namePtr, flags);
            }
            finally
            {
                Marshal.FreeHGlobal(namePtr);
            }
        }

        /// <summary>
        /// Find a typed object and increment its refcount. Don't forget to call __vlc_object_release()
        /// after using found object.
        /// </summary>
        [DllImport("libvlccore", ExactSpelling = true, EntryPoint = "vlc_object_find")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr vlc_object_find_unmanaged(IntPtr vlc_object_t, int i_type, int i_mode);

        /// <summary>
        /// Vlc_object_finds the specified vlc_object_t.
        /// </summary>
        /// <param name="vlc_object_t">The vlc_object_t.</param>
        /// <param name="i_type">The i_type.</param>
        /// <param name="i_mode">The i_mode.</param>
        /// <returns>IntPtr.</returns>
        public static IntPtr vlc_object_find(IntPtr vlc_object_t, int i_type, int i_mode)
        {
            if (CallTraceEnabled)
            {
                TraceCall("vlc_object_find");
            }
            return vlc_object_find_unmanaged(vlc_object_t, i_type, i_mode);
        }

        /// <summary>
        /// Decrement an object refcount
        /// * And destroy the object if its refcount reach zero.
        /// </summary>
        [DllImport("libvlccore", ExactSpelling = true, EntryPoint = "vlc_object_release")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void vlc_object_release_unmanaged(IntPtr vlc_object_t);

        /// <summary>
        /// Vlc_object_releases the specified vlc_object_t.
        /// </summary>
        /// <param name="vlc_object_t">The vlc_object_t.</param>
        public static void vlc_object_release(IntPtr vlc_object_t)
        {
            if (CallTraceEnabled)
            {
                TraceCall("vlc_object_release");
            }
            vlc_object_release_unmanaged(vlc_object_t);
        }

        /// <summary>
        /// Gets the list of children of an objects, and increment their reference count.
        /// </summary>
        /// <param name="p_object">Parent object</param>
        /// <returns>A list (possibly empty) or NULL in case of error.</returns>
        [DllImport("libvlccore", ExactSpelling = true, EntryPoint = "vlc_list_children")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr vlc_list_children_unmanaged(IntPtr p_object);

        /// <summary>
        /// Vlc_list_childrens the specified p_object.
        /// </summary>
        /// <param name="p_object">The p_object.</param>
        /// <returns>libvlc_list_t.</returns>
        public static libvlc_list_t vlc_list_children(IntPtr p_object)
        {
            if (CallTraceEnabled)
            {
                TraceCall("vlc_list_children");
            }
            var ptrRet = vlc_list_children_unmanaged(p_object);
            return (libvlc_list_t)(Marshal.PtrToStructure(ptrRet, typeof(libvlc_list_t)));
        }

        /// <summary>
        /// Var_s the set_unmanaged.
        /// </summary>
        /// <param name="p_object">The p_object.</param>
        /// <param name="psz_name">The psz_name.</param>
        /// <param name="value">The value.</param>
        [DllImport("libvlccore", ExactSpelling = true, EntryPoint = "var_Set")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void var_Set_unmanaged(IntPtr p_object, IntPtr psz_name, vlc_value_t value);

        /// <summary>
        /// Var_s the set.
        /// </summary>
        /// <param name="p_object">The p_object.</param>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        /// <exception cref="System.ArgumentNullException">name</exception>
        public static void var_Set(IntPtr p_object, string name, vlc_value_t value)
        {
            if (CallTraceEnabled)
            {
                TraceCall("var_Set");
            }
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            var namePtr = Marshal.StringToHGlobalAnsi(name);
            try
            {
                var_Set_unmanaged(p_object, namePtr, value);
            }
            finally
            {
                Marshal.FreeHGlobal(namePtr);
            }
        }

        /// <summary>
        /// Var_s the get_unmanaged.
        /// </summary>
        /// <param name="p_object">The p_object.</param>
        /// <param name="psz_name">The psz_name.</param>
        /// <param name="value">The value.</param>
        [DllImport("libvlccore", ExactSpelling = true, EntryPoint = "var_Get")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void var_Get_unmanaged(IntPtr p_object, IntPtr psz_name, ref vlc_value_t value);

        /// <summary>
        /// Var_s the get.
        /// </summary>
        /// <param name="p_object">The p_object.</param>
        /// <param name="name">The name.</param>
        /// <param name="value">The value.</param>
        /// <exception cref="System.ArgumentNullException">name</exception>
        public static void var_Get(IntPtr p_object, string name, ref vlc_value_t value)
        {
            if (CallTraceEnabled)
            {
                TraceCall("var_Get");
            }
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            var namePtr = Marshal.StringToHGlobalAnsi(name);
            try
            {
                var_Get_unmanaged(p_object, namePtr, ref value);
            }
            finally
            {
                Marshal.FreeHGlobal(namePtr);
            }
        }

        #endregion

        #region Core

        /// <summary>
        /// Libvlc_new_unmanageds the specified argc.
        /// </summary>
        /// <param name="argc">The argc.</param>
        /// <param name="argv">The argv.</param>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_new")]
        [SuppressUnmanagedCodeSecurity]
        public static extern IntPtr libvlc_new_unmanaged(int argc, IntPtr argv);

        /// <summary>
        /// Libvlc_new_unmanageds the specified argc.
        /// </summary>
        /// <param name="argc">The argc.</param>
        /// <param name="argv">The argv.</param>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_new")]
        [SuppressUnmanagedCodeSecurity]
        public static extern IntPtr libvlc_new_unmanaged(int argc, [MarshalAs(UnmanagedType.LPArray)] String[] argv);

        /// <summary>
        /// Libvlc_new_unmanageds the specified argc.
        /// </summary>
        /// <param name="argc">The argc.</param>
        /// <param name="argv">The argv.</param>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_new")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_new_unmanaged(int argc, IntPtr[] argv);

        /// <summary>
        /// Libvlc_news the specified parameters.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <returns>IntPtr.</returns>
        /// <exception cref="RuanRang.Media.Core.Exceptions.VlcInternalException"></exception>
        public static IntPtr libvlc_new(string[] parameters)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_new");
            }
            //
            // Check the libvlc version before continuing
            var libvlcVersion = libvlc_get_version();
            //#if DEBUG
            //            const string expectedPrefix = "1.1.";
            //            Debug.Assert(libvlcVersion.StartsWith(expectedPrefix), String.Format("libvlc {0}x is required, but \"{1}\" is present", expectedPrefix, libvlcVersion));
            //#endif
            Trace.WriteLine(String.Format("using libvlc version \"{0}\"", libvlcVersion));
            var intPtrArray = new IntPtr[parameters.Length];// IntPtr.Zero;
            for (var i = 0; i < intPtrArray.Length; i++)
            {
                var bytes = Encoding.UTF8.GetBytes(parameters[i]);
                var pMrl = Marshal.AllocHGlobal(bytes.Length + 1);
                Marshal.Copy(bytes, 0, pMrl, bytes.Length);
                Marshal.WriteByte(pMrl, bytes.Length, 0);
                intPtrArray[i] = pMrl;
                if (pMrl != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pMrl);
                }
            }
            var descriptor = libvlc_new_unmanaged(parameters.Length, intPtrArray);
            if (IntPtr.Zero == descriptor)
            {
                var message = libvlc_errmsg();
                if (string.IsNullOrEmpty(message))
                {
                    message = "Cannot create instance of libvlc.";
                }
                throw new VlcInternalException(message);
            }
            return descriptor;
        }

        /// <summary>
        /// Libvlc_get_version_unmanageds this instance.
        /// </summary>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_get_version")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_get_version_unmanaged();

        /// <summary>
        /// Libvlc_get_versions this instance.
        /// </summary>
        /// <returns>String.</returns>
        public static String libvlc_get_version()
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_get_version");
            }
            var version = libvlc_get_version_unmanaged();
            return Marshal.PtrToStringAnsi(version);
        }

        /// <summary>
        /// Decrement the reference count of a libvlc instance, and destroy it if it reaches zero
        /// </summary>
        /// <param name="libvlc_instance_t">The libvlc_instance_t.</param>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_release")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void libvlc_release_unmanaged(IntPtr libvlc_instance_t);

        /// <summary>
        /// Libvlc_releases the specified libvlc_instance_t.
        /// </summary>
        /// <param name="libvlc_instance_t">The libvlc_instance_t.</param>
        public static void libvlc_release(IntPtr libvlc_instance_t)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_release");
            }
            libvlc_release_unmanaged(libvlc_instance_t);
        }

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
        public static extern string libvlc_get_compiler();

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
        public static extern string libvlc_get_changeset();
        #endregion

        #region Events

        #region Delegates

        /// <summary>
        /// Delegate VlcEventHandlerDelegate
        /// </summary>
        /// <param name="libvlc_event">The libvlc_event.</param>
        /// <param name="userData">The user data.</param>
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void VlcEventHandlerDelegate(IntPtr libvlc_event, IntPtr userData);

        #endregion

        /// <summary>
        /// Libvlc_event_attach_unmanageds the specified p_event_manager.
        /// </summary>
        /// <param name="p_event_manager">The p_event_manager.</param>
        /// <param name="i_event_type">The i_event_type.</param>
        /// <param name="f_callback">The f_callback.</param>
        /// <param name="user_data">The user_data.</param>
        /// <returns>System.Int32.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_event_attach")]
        [SuppressUnmanagedCodeSecurity]
        private static extern int libvlc_event_attach_unmanaged(IntPtr p_event_manager, libvlc_event_type_e i_event_type, IntPtr f_callback, IntPtr user_data);

        /// <summary>
        /// Libvlc_event_attaches the specified p_event_manager.
        /// </summary>
        /// <param name="p_event_manager">The p_event_manager.</param>
        /// <param name="i_event_type">The i_event_type.</param>
        /// <param name="f_callback">The f_callback.</param>
        /// <param name="user_data">The user_data.</param>
        /// <returns>System.Int32.</returns>
        public static int libvlc_event_attach(IntPtr p_event_manager, libvlc_event_type_e i_event_type, IntPtr f_callback, IntPtr user_data)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_event_attach");
            }
            return libvlc_event_attach_unmanaged(p_event_manager, i_event_type, f_callback, user_data);
        }

        /// <summary>
        /// Libvlc_event_attaches the specified p_event_manager.
        /// </summary>
        /// <param name="p_event_manager">The p_event_manager.</param>
        /// <param name="i_event_type">The i_event_type.</param>
        /// <param name="f_callback">The f_callback.</param>
        /// <param name="user_data">The user_data.</param>
        /// <returns></returns>
        public static int libvlc_event_attach(IntPtr p_event_manager, libvlc_event_type_e i_event_type, VlcEventHandlerDelegate f_callback, IntPtr user_data)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_event_attach");
            }
            var interop = Marshal.GetFunctionPointerForDelegate(f_callback);
            return libvlc_event_attach_unmanaged(p_event_manager, i_event_type, interop, user_data);
        }

        /// <summary>
        /// Libvlc_event_detach_unmanageds the specified p_event_manager.
        /// </summary>
        /// <param name="p_event_manager">The p_event_manager.</param>
        /// <param name="i_event_type">The i_event_type.</param>
        /// <param name="f_callback">The f_callback.</param>
        /// <param name="user_data">The user_data.</param>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_event_detach")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void libvlc_event_detach_unmanaged(IntPtr p_event_manager, libvlc_event_type_e i_event_type, IntPtr f_callback, IntPtr user_data);

        /// <summary>
        /// Libvlc_event_detaches the specified p_event_manager.
        /// </summary>
        /// <param name="p_event_manager">The p_event_manager.</param>
        /// <param name="i_event_type">The i_event_type.</param>
        /// <param name="f_callback">The f_callback.</param>
        /// <param name="user_data">The user_data.</param>
        public static void libvlc_event_detach(IntPtr p_event_manager, libvlc_event_type_e i_event_type, IntPtr f_callback, IntPtr user_data)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_event_detach");
            }
            libvlc_event_detach_unmanaged(p_event_manager, i_event_type, f_callback, user_data);
        }

        /// <summary>
        /// Libvlc_event_detaches the specified p_event_manager.
        /// </summary>
        /// <param name="p_event_manager">The p_event_manager.</param>
        /// <param name="i_event_type">The i_event_type.</param>
        /// <param name="f_callback">The f_callback.</param>
        /// <param name="user_data">The user_data.</param>
        public static void libvlc_event_detach(IntPtr p_event_manager, libvlc_event_type_e i_event_type, VlcEventHandlerDelegate f_callback, IntPtr user_data)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_event_detach");
            }
            var interop = Marshal.GetFunctionPointerForDelegate(f_callback);
            libvlc_event_detach_unmanaged(p_event_manager, i_event_type, interop, user_data);
        }

        /// <summary>
        /// Libvlc_event_type_name_unmanageds the specified event_type.
        /// </summary>
        /// <param name="event_type">The event_type.</param>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_event_type_name")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_event_type_name_unmanaged(libvlc_event_type_e event_type);

        /// <summary>
        /// Libvlc_event_type_names the specified event_type.
        /// </summary>
        /// <param name="event_type">The event_type.</param>
        /// <returns>System.String.</returns>
        public static string libvlc_event_type_name(libvlc_event_type_e event_type)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_event_type_name");
            }
            var strPtr = libvlc_event_type_name_unmanaged(event_type);
            return Marshal.PtrToStringAnsi(strPtr);
        }

        #endregion
    }
}